"""

Roket Secure Language - List of builtin definitions

"""

import RSB
from numbers import Number

"""
# Builtin Class Base
"""
class BuiltinC(RSB.Obj):
	def __init__(self, type):
		self.type = type
		self.variables = dict()
		self.functions = dict()
		self.events = dict()
		self.constructor = None
	
	def clone(self):
		# This function is used when a new instance of the class is created.
		self.error("This object can not have instances.")
		return None
	
	def error(self, engine, error_text):
		engine.fatalError(0, engine.current_line_data, error_text)

"""
# Builtin Function Base
"""
class BuiltinF(RSB.Func):
	def __init__(self, name, args):
		self.name = name
		self.args = args
		self.code = ""
	
	def execute(self, engine, args):
		pass
	
	def error(self, engine, error_text):
		engine.fatalError(0, engine.current_line_data, error_text)

	def BCReturn(self, obj, engine):
		if (isinstance(obj, Number)):
			engine.return_scope = float(obj) # always convert back to float for RSL
		elif (isinstance(obj, bool)):
			engine.return_scope = float(obj) # always convert back to float for RSL
		else:
			engine.return_scope = obj
	
	def BCFunc(self, func_name, func_args, engine):
		# Check to see if the number of arguments passed matches the arguments defined
		if (len(func_args) != len(engine.functions[func_name].args)):
			self.error(engine, "Argument count mismatch while calling '" + func_name + "' (" + str(len(engine.functions[func_name].args)) + " defined, " + str(len(func_args)) + " provided)");
	
		# Store the current global variable scope and register in a temporary variable - we will set it back later.
		global_variables = engine.variables
		global_register = engine.register
		engine.variables = dict()
		engine.register = dict()
		
		# Call the function
		engine.functions[func_name].execute(engine, func_args);
		
		# Set the variable scope and registers back again.
		engine.variables = global_variables
		engine.register = global_register
	
	def BCClassConstructor(self, class_obj, func_args, engine):
		# Check to see if the number of arguments passed matches the arguments defined
		if (len(func_args) != len(class_obj.constructor.args)):
			self.error(engine, "Argument count mismatch while calling constructor for '" + class_obj.type + "' (" + str(len(class_obj.constructor.args)) + " defined, " + str(len(func_args)) + " provided)");
	
		# Store the current global variable scope and register in a temporary variable - we will set it back later.
		global_variables = engine.variables
		global_register = engine.register
		engine.variables = dict()
		engine.register = dict()
		
		# Call the function
		class_obj.constructor.execute(engine, func_args);
		
		# Set the variable scope and registers back again.
		engine.variables = global_variables
		engine.register = global_register

"""
# Builtins Controller
"""
class BuiltinController():
	def load(self, engine):
		# Load all the builtins into the engine.
		engine.functions["print"] = PrintBIF("print", ["str"]);
		engine.functions["list"] = ListBIF("list", []);
		engine.functions["len"] = LenBIF("list", ["list"]);
		engine.functions["str"] = StrBIF("str", ["var"]);
		engine.functions["float"] = FloatBIF("float", ["var"]);
		
		# Load our classes and constructors
		import MapBIC
		
		engine.classes["map"] = MapBIC.MapBIC("map")
		engine.functions["map"] = NewClassKW("map", list())
	
	def unload(self, engine):
		# Unload all the builtins from the engine.
		del engine.functions["print"];
		del engine.functions["list"];
		del engine.functions["len"];
		del engine.functions["str"];
		del engine.functions["float"];
	
	def loadConstants(self, engine):
		# Load the constants such as True and False
		# into the variable list.
		
		engine.variables["True"] = float(1)
		engine.variables["False"] = float(0)
		
		import MathBIC
		engine.variables["math"] = MathBIC.MathBIC("math")
		
		return ["True", "False", "math"]

"""
# Builtin Functions
"""
class PrintBIF(BuiltinF):
	def execute(self, engine, args):
		if (len(args) != 1):
			self.error(engine, "Invalid number of arguments for print.")
			return
	
		if (isinstance(args[0], RSB.Obj)):
			print "<" + args[0].type + " instance>"
		else:
			print args[0]

class LenBIF(BuiltinF):
	def execute(self, engine, args):
		if (len(args) != 1):
			self.error(engine, "Invalid number of arguments for len.")
			return
	
		if (not isinstance(args[0], list)):
			self.error(engine, "Argument #1 '" + str(args[0]) + "' is not a list.");
		else:
			self.BCReturn(float(len(args[0])), engine)

class StrBIF(BuiltinF):
	def execute(self, engine, args):
		if (len(args) != 1):
			self.error(engine, "Invalid number of arguments for str.")
			return
	
		if (isinstance(args[0], RSB.Obj)):
			self.BCReturn("<" + args[0].type + " instance>", engine)
		else:
			self.BCReturn(str(args[0]), engine)

class FloatBIF(BuiltinF):
	def execute(self, engine, args):
		if (len(args) != 1):
			self.error(engine, "Invalid number of arguments for float.")
			return
	
		try:
			self.BCReturn(float(args[0]), engine)
		except:
			self.error(engine, "Unable to convert argument #1 '" + str(args[0]) + "' into a float.");

class ListBIF(BuiltinF):
	def execute(self, engine, args):
		# Returns a new list
		self.BCReturn(list(), engine)

class NewClassKW(BuiltinF):
	def __init__(self, name, args):
		super(BuiltinF, self).__init__(name, args)
		self.code = "CLASS CONSTRUCTOR"
	
	def execute(self, engine, args):
		if (self.name not in engine.classes):
			self.error(engine, "Undefined class '" + self.name + "'.");
		else:
			obj = engine.classes[self.name].clone()
			
			if (obj.constructor != None):
				# Add reference to object
				# in the argument list
				args.insert(0, obj)
				
				self.BCClassConstructor(obj, args, engine)
			
			self.BCReturn(obj, engine)